#include "CBlockSceneNode.h"
#include "CRegionSceneNode.h"
#include "CTriangleSelector.h"

CBlockSceneNode::CBlockSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id, CRegionSceneNode *region, const core::vector3di& globalposition, const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale)
	: ISceneNode(parent, mgr, id, position, rotation, scale), Region(region), GlobalPosition(globalposition), TilesUpdated(false), HiddenTileCount(0)
{
//	setName(stringc("Block ") + stringc((s32)position.X) + "," + stringc((s32)position.Y) + "," + stringc((s32)position.Z));
	//this->setTriangleSelector(mgr->createMetaTriangleSelector());

//	Tiles = new STile*[256];

	//for(u32 i = 0; i < 256; i++)
	//{
		//Tiles[i] = new STile();
	//	Tiles[i] = new CTileSceneNode(this, mgr, -1, this, vector3df((f32)(i % 16), 0, (f32)(i / 17)));
		/*Tiles[i].Positioni = vector3di(i % 16, 0, i / 16);
		//Tiles[i].Positioni = vector3di(i % 16, i / 16, (s32)position.Z);
		Tiles[i].Position = vector3df((f32)Tiles[i].Positioni.X, (f32)Tiles[i].Positioni.Y, (f32)Tiles[i].Positioni.Z);
		Tiles[i].Removed = false;
		Tiles[i].Visible = true;
	//	Tiles[i].Visible = rand() > 1000 ? false: true;
		//Tiles[i].Box = aabbox3d<f32>(Tiles[i].Position, Tiles[i].Position + vector3df(1,1,1));
		Tiles[i].Box = aabbox3d<f32>(vector3df(0,0,0), vector3df(1,1,1));
		Tiles[i].TransformedBox = Tiles[i].Box;
		Tiles[i].TransformedBox.MinEdge += Tiles[i].Position + Position;
		Tiles[i].TransformedBox.MaxEdge += Tiles[i].Position + Position;
		Tiles[i].Block = this;*/
	//}

	BoundingBox.MinEdge = vector3df(0, 0, 0);
	BoundingBox.MaxEdge = vector3df(16, 1, 16);
	TransformedBoundingBox = BoundingBox;
	this->getAbsoluteTransformation().transformBoxEx(TransformedBoundingBox);
}

CBlockSceneNode::~CBlockSceneNode()
{
#ifdef USE_IRR_MAP
	map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
	for(; !it.atEnd(); it++)
	{
		delete it.getNode()->getValue();
	}
#else
	u32 c = Tiles.size();
	for(u32 i = 0; i < c; i++)
		delete Tiles[i];
#endif
	Tiles.clear();

	//ISceneNode::~ISceneNode();
}

bool CBlockSceneNode::isVisible()
{
	if(!IsVisible) return false;
	//for(u32 i = 0; i < Tiles.size(); i++)
	//	if(Tiles[i]->Visible)
	//		return true;
	return true;
}

void CBlockSceneNode::optimizeCollisionData(bool reset)
{
	//if(!TilesUpdated)
	//	updateTiles();
	if(TriangleSelector && !reset) return;

	if(!TriangleSelector)
		TriangleSelector = SceneManager->createMetaTriangleSelector();
	else
		((IMetaTriangleSelector*)TriangleSelector)->removeAllTriangleSelectors();

	bool simple = true;

	u16 tiletype = 0;
	u32 visiblecount = 0;
	CTile *tile = 0;
	u32 c = Tiles.size();
#ifdef USE_IRR_MAP
	map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
	for(; !it.atEnd(); it++)
	{
		CTile *tile = it.getNode()->getValue();
#else
	for(u32 i = 0; i < c; i++)
	{
		CTile *tile = Tiles[i];
#endif
		if(tile->Visible)
		{
			visiblecount++;
			if(tiletype != 0 && tiletype != tile->TileType)
				simple = false;
			tiletype = tile->TileType;
		}
	}

	if(simple && visiblecount != c)
		simple = false;

	//simple = false;
	if(simple && tile)
	{
		aabbox3df box = tile->Resource->Box;
		this->getAbsoluteTransformation().transformBoxEx(box);
		CTriangleSelector *selector = new CTriangleSelector(box);
		((IMetaTriangleSelector*)TriangleSelector)->addTriangleSelector(selector);
		selector->drop();
	}
	else
	{
#ifdef USE_IRR_MAP
		map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
		for(; !it.atEnd(); it++)
		{
			tile = it.getNode()->getValue();
#else
		for(u32 i = 0; i < c; i++)
		{
			tile = Tiles[i];
#endif
			if(tile->Visible)
			{
				ITriangleSelector *selector = tile->getTriangleSelector();
				if(selector)
					((IMetaTriangleSelector*)TriangleSelector)->addTriangleSelector(selector);
			}
		}
	}
}

void CBlockSceneNode::render()
{
	if(!TilesUpdated)
		updateTiles();

	matrix4 m0 = this->getAbsoluteTransformation();

	IVideoDriver *video = SceneManager->getVideoDriver();
	video->setMaterial(IdentityMaterial);

#ifdef USE_IRR_MAP
	map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
	for(; !it.atEnd(); it++)
	{
		CTile *tile = it.getNode()->getValue();
#else
	u32 c = Tiles.size();
	for(u32 i = 0; i < c; i++)
	{
		CTile *tile = Tiles[i];
#endif
		if(tile->Visible && tile->Resource && tile->Resource->Mesh)
		{
		//	if(tile->getGeoLayerMaterial())
		//	{
		//	}
			if(Region->getWireframe())
			{
	//			if(!tile->Lighting)
//					color = SColor(255, 0, 0, 0);
				video->setTransform(ETS_WORLD, tile->getAbsoluteTransformation());
				if(!tile->Lighting)
				{
					SMaterial mat = IdentityMaterial;
					mat.Wireframe = false;
					mat.setTexture(0, video->getTexture("default"));
					video->setMaterial(mat);
				}
				video->draw3DBox(tile->BoundingBox);
				video->setMaterial(IdentityMaterial);
			}
			else
			{
//				SMaterial mat = IdentityMaterial;
	//			if(tile->Material)
		//			mat = tile->Material->Material;
			//	video->setMaterial(mat);
				matrix4 m = tile->getAbsoluteTransformation() * tile->Resource->getTransformation();
				video->setTransform(ETS_WORLD, m);
				u32 mbc = tile->Resource->Mesh->getMeshBufferCount();
				for(u32 mbi = 0; mbi < mbc; mbi++)
				{
					IMeshBuffer *mb = tile->Resource->Mesh->getMeshBuffer(mbi);
					SMaterial mat = mb->getMaterial();//tile->Resource->Mesh->getMeshBuffer(mbi)->getMaterial();

					if(tile->Material)
					{
						mat = tile->Material->Material;
						/*SMaterial tm = tile->Material->Material;
						for(u32 ti = 0; ti < 4; ti++)
						{
							ITexture *tit = tm.getTexture(ti);
							if(tit)
								mat.setTexture(ti, tit);
						}*/
				//		for(u32 tfi = 0; tfi <= 0x10000; tfi += tfi)
				//			mat.setFlag((E_MATERIAL_FLAG)tfi, tm.getFlag((E_MATERIAL_FLAG)tfi));
						//mat.AmbientColor = tm.AmbientColor;
					}
				//	mat.AmbientColor = mb->getMaterial().AmbientColor;
			//		if(tile->Resource->Texture)
			//			mat.setTexture(1, tile->Resource->Texture);
					mat.Lighting = tile->Lighting;
					video->setMaterial(mat);
					video->drawMeshBuffer(mb);
				}
			}
		}
	}

	c = Buildings.size();
	for(u32 i = 0; i < c; i++)
	{
		CBuilding *building = Buildings[i];
	//	printf("%f, %f, %f\n", building->RelativeTranslation.X, building->RelativeTranslation.Y, building->RelativeTranslation.Z);
		if(building->Visible && building->Resource && building->Resource->Mesh)
		{
			if(Region->getWireframe())
			{
				video->setTransform(ETS_WORLD, building->getAbsoluteTransformation());
				if(!building->Lighting)
				{
					SMaterial mat = IdentityMaterial;
					mat.Wireframe = false;
					mat.setTexture(0, video->getTexture("default"));
					video->setMaterial(mat);
				}
				video->draw3DBox(building->BoundingBox);
				video->setMaterial(IdentityMaterial);
			}
			else
			{
				matrix4 m = building->getAbsoluteTransformation() * building->Resource->getTransformation();
				video->setTransform(ETS_WORLD, m);
				u32 mbc = building->Resource->Mesh->getMeshBufferCount();
				for(u32 mbi = 0; mbi < mbc; mbi++)
				{
					IMeshBuffer *mb = building->Resource->Mesh->getMeshBuffer(mbi);
					SMaterial mat = mb->getMaterial();

					if(building->Material)
					{
						mat = building->Material->Material;
					}
					if(building->Resource->Texture)
						mat.setTexture(1, building->Resource->Texture);
					mat.Lighting = building->Lighting;

					video->setMaterial(mat);
					video->drawMeshBuffer(mb);
				}
			}
		}
	}
//	video->setTransform(ETS_WORLD, m0);
//	video->draw3DBox(BoundingBox);



//	for(u32 i = 0; i < TilesToRender.size(); i++)
//		TilesToRender[i]->render();

	/*bool usesimpletriangleselector = true;
	bool update = false;
	SResource *resource = 0;
	s16 lasttype = -1;
	for(u32 i = 0; i < 256; i++)
	{
		if(Tiles[i].Visible && !Tiles[i].Removed)
		{
			if(Tiles[i].UpdateTileType)
			{
				update = true;
				Tiles[i].Resource = Region->getTileResource(Tiles[i].TileType);
				Tiles[i].UpdateTileType = false;

				if(Tiles[i].Resource != 0)
				{
					resource = Tiles[i].Resource;
					matrix4 m;
					m.setScale(Tiles[i].Resource->Scale);
					Tiles[i].Box = aabbox3df(vector3df(0,0,0), vector3df(1,1,1));
					Tiles[i].TransformedBox = Tiles[i].Box;
					m.transformBoxEx(Tiles[i].Box);
					m.setTranslation(Tiles[i].Position + getAbsolutePosition());
					m.transformBoxEx(Tiles[i].TransformedBox);

					if(Tiles[i].TriangleSelector)
					{
						((CTriangleSelector*)Tiles[i].TriangleSelector)->update();
					}
					else
					{
						Tiles[i].TriangleSelector = new CTriangleSelector(&Tiles[i]);
						((IMetaTriangleSelector*)this->getTriangleSelector())->addTriangleSelector(Tiles[i].TriangleSelector);
					}
				}

				if(lasttype != -1 && Tiles[i].TileSizeType != lasttype)
					usesimpletriangleselector = false;
				lasttype = Tiles[i].TileSizeType;
			}

			if(Tiles[i].Resource != 0 && Tiles[i].Resource->Mesh != 0)
			{

				mat = this->getAbsoluteTransformation();
				mat.setRotationDegrees(Tiles[i].Resource->Rotation);
				mat.setTranslation(Position + Tiles[i].Position + Tiles[i].Resource->Position);

				if(Tiles[i].Resource->Scale != vector3df(1,1,1))
				{
					matrix4 smat;
					smat.setScale(Tiles[i].Resource->Scale);
					mat *= smat;
				}
				mat.setScale(0.5f);

				video->setTransform(ETS_WORLD, mat);

				SMaterial mat = Tiles[i].Resource->Material;
				mat.Lighting = Tiles[i].Lighting;
				video->setMaterial(mat);
				for(u32 mb = 0; mb < Tiles[i].Resource->Mesh->getMeshBufferCount(); mb++)
					video->drawMeshBuffer(Tiles[i].Resource->Mesh->getMeshBuffer(mb));

			}
		}
		else
			usesimpletriangleselector = false;
	}

	if(update && usesimpletriangleselector)
	{
		aabbox3df box = this->getBoundingBox();
		if(resource != 0)
		{
			matrix4 m;
			m.setTranslation(this->getAbsolutePosition());
			m.setScale(resource->Scale);
			m.transformBoxEx(box);

		}
		((IMetaTriangleSelector*)TriangleSelector)->removeAllTriangleSelectors();
		CTriangleSelector *selector = new CTriangleSelector(box);
		((IMetaTriangleSelector*)TriangleSelector)->addTriangleSelector(selector);
		selector->drop();
	}*/
}

void CBlockSceneNode::renderBuildings()
{
	IVideoDriver *video = SceneManager->getVideoDriver();
	u32 c = Buildings.size();
	for(u32 i = 0; i < c; i++)
	{
		CBuilding *building = Buildings[i];
		if(building->Visible && building->Resource && building->Resource->Mesh)
		{
		/*	if(Region->getWireframe())
			{
				video->setTransform(ETS_WORLD, building->getAbsoluteTransformation());
				if(!building->Lighting)
				{
					SMaterial mat = IdentityMaterial;
					mat.Wireframe = false;
					mat.setTexture(0, video->getTexture("default"));
					video->setMaterial(mat);
				}
				video->draw3DBox(building->BoundingBox);
				video->setMaterial(IdentityMaterial);
			}
			else*/
			{
				matrix4 m = building->getAbsoluteTransformation() * building->Resource->getTransformation();
				video->setTransform(ETS_WORLD, m);
				u32 mbc = building->Resource->Mesh->getMeshBufferCount();
				for(u32 mbi = 0; mbi < mbc; mbi++)
				{
					IMeshBuffer *mb = building->Resource->Mesh->getMeshBuffer(mbi);
					SMaterial mat = mb->getMaterial();

					if(building->Material)
					{
						mat = building->Material->Material;
					}
					if(building->Resource->Texture)
						mat.setTexture(1, building->Resource->Texture);
					mat.Lighting = building->Lighting;

					video->setMaterial(mat);
					video->drawMeshBuffer(mb);
				}
			}
		}
	}
}

void CBlockSceneNode::addTile(vector3df position, u16 tiletype, SBasicMaterial *m)
{
	CTile *tile = new CTile(this);
//	tile->RelativeTranslation = position;
//	tile->RelativeTranslation.X = 15-tile->RelativeTranslation.X;
	tile->Position = STilePosition((unsigned char)(15 - position.X), (unsigned char)position.Y, (unsigned char)position.Z);
	tile->setTileType(tiletype);
	tile->Material = m;
	//tile->setMaterialIndex(materialindex);
#ifdef USE_IRR_MAP
	Tiles[vector2di((s32)position.X, (s32)position.Z)] = tile;
#else
	Tiles.push_back(tile);
#endif
}

void CBlockSceneNode::addBuilding(vector3df position, u32 type, SBasicMaterial *m)
{
	CBuilding *building = new CBuilding(this);
	building->RelativeTranslation = position;
	building->RelativeTranslation.X = 15-building->RelativeTranslation.X;
	building->setBuildingType(type);
	building->Material = m;
	Buildings.push_back(building);
}

void CBlockSceneNode::showTile(CTile *tile, bool show)
{
#ifdef USE_IRR_MAP
	map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
	for(; !it.atEnd(); it++)
	{
		CTile *t = it.getNode()->getValue();
		if(t == tile)
		{
			t->Visible = show;
			return;
		}
	}
#else
	for(u32 i = 0; i < Tiles.size(); i++)
	{
		if(Tiles[i] == tile)
		{
			Tiles[i]->Visible = show;
			return;
		}
	}
#endif
}

CTile* CBlockSceneNode::getTileFromLine(line3df line)
{
	f32 dist = -1;
	CTile *ret = 0;

#ifdef USE_IRR_MAP
	map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
	for(; !it.atEnd(); it++)
	{
		CTile *tile = it.getNode()->getValue();
		if(tile->Visible)
		{
			aabbox3df box = tile->TransformedBoundingBox;
			if(box.intersectsWithLine(line))
			{
				f32 len = line3df(box.getCenter(), line.start).getLength();
				if(dist == -1 || len < dist)
				{
					dist = len;
					ret = tile;
				}
			}
		}
	}
#else
	u32 c = Tiles.size();
	for(u32 i = 0; i < c; i++)
	{
		if(Tiles[i]->Visible)// && Tiles[i].Resource != 0)
		{
			aabbox3df box = Tiles[i]->TransformedBoundingBox;
			if(box.intersectsWithLine(line))
			{
				f32 len = line3df(box.getCenter(), line.start).getLength();
				if(dist == -1 || len < dist)
				{
					/*ITriangleSelector *sel = Tiles[i]->getTriangleSelector();
					s32 c = sel->getTriangleCount();
					if(c > 0)
					{
						triangle3df *tris = new triangle3df[c];
						matrix4 m = Region->getAbsoluteTransformation();
						sel->getTriangles(tris, c, c, &m);
						vector3df intersection;
						for(s32 i = 0; i < c; i++)
							if(tris[i].getIntersectionWithLine(line.start, line.getVector(), intersection))
							{
								dist = len;
								ret = Tiles[i];
							}
					}
					else*/
					{
						dist = len;
						ret = Tiles[i];
					}
				}
			}
		}
	}
#endif
	return ret;
}

bool CBlockSceneNode::intersectsWithBox(aabbox3df box, bool accurate)
{
	if(this->getTransformedBoundingBox().intersectsWithBox(box))
	{
		if(accurate)
		{
#ifdef USE_IRR_MAP
			map<vector2di, CTile*>::Iterator it = Tiles.getIterator();
			for(; !it.atEnd(); it++)
			{
				CTile *tile = it.getNode()->getValue();
				if(tile->Visible)
				{
					if(tile->TransformedBoundingBox.intersectsWithBox(box))
						return true;
				}
			}
#else
			u32 c = Tiles.size();
			for(u32 i = 0; i < c; i++)
			{
				if(Tiles[i]->Visible)
					if(Tiles[i]->TransformedBoundingBox.intersectsWithBox(box))
						return true;
			}
#endif
		}
		else
		{
			return true;
		}
	}
	return false;
}

CTile* CBlockSceneNode::getTile(s32 x, s32 y) const
{
	if(x < 0 || y < 0 || x > 15 || y > 15) return 0;
	u32 c = Tiles.size();
	for(u32 i = 0; i < c; i++)
//		if(Tiles[i] && Tiles[i]->RelativeTranslation.X == x && Tiles[i]->RelativeTranslation.Z == y)
		if(Tiles[i] && Tiles[i]->Position.X == x && Tiles[i]->Position.Z == y)
			return Tiles[i];
	return 0;
}

void CBlockSceneNode::updateTiles()
{
	TilesUpdated = true;
	u32 c = Tiles.size();
	for(u32 i = 0; i < c; i++)
		Tiles[i]->updateTile();

	HiddenTileCount = 0;

	u32 tc = 0;
	for(u32 i = 0; i < c; i++)
	{
		vector3di v = Tiles[i]->getGlobalPosition();
		CTile *tiles[6] = { Region->getTile(v + vector3di(-1, 0, 0)),
							Region->getTile(v + vector3di(1, 0, 0)),
							Region->getTile(v + vector3di(0, -1, 0)),
							Region->getTile(v + vector3di(0, 1, 0)),
							Region->getTile(v + vector3di(0, 0, -1)),
							Region->getTile(v + vector3di(0, 0, 1)) };

		bool hide = true;
		for(u32 vi = 0; vi < 6; vi++)
		{
			if(!tiles[vi] || !DFHack::isWallTerrain(tiles[vi]->TileType))
			{
				hide = false;
				break;
			}
		}
		if(hide)
		{
			Tiles[i]->Visible = false;
			HiddenTileCount++;
		}
	}

	optimizeCollisionData();
}